package com.neusoft.databus.common.bean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import javax.validation.constraints.NotBlank;

import org.springframework.util.StringUtils;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.neusoft.databus.common.connection.AbstractConnection;
import com.neusoft.databus.common.connection.ConnectionFactory;
import com.neusoft.databus.common.connection.SerialConnection;
import com.neusoft.databus.common.connection.SocketConnection;
import com.neusoft.databus.common.enums.MetricUsageType;
import com.neusoft.databus.common.enums.PhysicalConnType;
import com.neusoft.databus.common.enums.Protocol;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * 面向采集任务的抽象命令 <br>
 * 1, 连接配置信息<->在 @Gateway 实体配置<br>
 * 2, 连接方式<->由 @Gateway 实体管理<br>
 * 3, 数据协议<->在 @MetricGroup 实体配置<br>
 * 4, 指标信息<->由 @Metric 实体管理<br>
 *
 * @author sunchf
 * @date 2019年1月7日 上午10:33:00
 */

@Getter
@Setter
@ToString
public class CommandBean implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotBlank(message = "网关连接类型不能为空")
    private PhysicalConnType connType;
    @NotBlank(message = "网关连接配置不能为空")
    private String connectionStr;
    private Integer connectTimeout = 3000;
    private Integer connectRetry = 0;
    private String gateway;
    /** 资源信息，从资源获取 */
    private List<ResourceBean> resourceBean = new ArrayList<>();
    /** 协议信息，从指标组获取 */
    @NotBlank(message = "数据协议类型不能为空")
    private Protocol protocol;
    /** 协议扩展属性，当protocol=PLUBIN时用。从指标组获取 */
    private String protocolConfig;
    /** 指令用途，从指标组获取 */
    private MetricUsageType usageType;
    /** 任务启动时间 */
    private Date beginTime;
    /** 任务结束时间 */
    private Date endTime;
    /** 采集开始时间 */
    private Date collectBeginTime;
    /** 采集结束时间 */
    private Date collectEndTime;
    /** 调试标识。当设置调试时，不存储数据 */
    private boolean forTest = false;
    /** 异步采集标识 */
    private boolean async = true;
    /** 错误码。当任务执行过程中发生错误时设置 */
    private String errorCode;
    /** 错误信息。当任务执行过程中发生错误时设置 */
    private String errorMessage;

    @JsonIgnore
    private AbstractConnection connection;

    public AbstractConnection getConnection() {
        if (null == this.connection) {
            this.connection = ConnectionFactory.createConnectionInfo(this.connType, this.connectionStr);
            // 兼容超时时间以秒为单位的版本
            this.connection
                    .setConnectTimeout(this.connectTimeout < 100 ? this.connectTimeout * 1000 : this.connectTimeout);
            this.connection.setConnectRetry(this.connectRetry);
        }
        return this.connection;
    }

    /**
     * 网关标识符 <br />
     * 在socket和serial连接时，微服务调用需要记住路由状态
     *
     * @return
     */
    public String getGatewayIdentifier() {
        String identifier = "";
        if (PhysicalConnType.SOCKET.equals(this.connType)) {
            identifier = ((SocketConnection) this.getConnection()).getIpAddress();
        } else if (PhysicalConnType.SERIAL.equals(this.connType)) {
            identifier = ((SerialConnection) this.getConnection()).getComValue();
        }

        return identifier;
    }

    /**
     * 采集结果向外传递时，清空非核心数据项
     */
    public void clearNotCoreFileds() {
        this.resourceBean.forEach(r -> {
            r.metrics.forEach(it -> {
                //it.setVariable(null);
                it.setValueType(null);
                it.setRule(null);
                it.setCommand(null);
            });
        });
    }

    /**
     * 生成日志跟踪ID
     *
     * @return
     */
    public String generateTraceId() {
        final StringBuilder sb = new StringBuilder();
        sb.append(this.connType.toString().toUpperCase());
        sb.append("-");

        if (this.resourceBean.size() > 1) {
            sb.append(this.gateway.toUpperCase());
        } else {
            sb.append(this.resourceBean.get(0).getCode().toUpperCase());
        }
        sb.append("-");
        sb.append(this.beginTime.getTime());
        return sb.toString();
    }

    public boolean isError() {
        return !StringUtils.isEmpty(this.errorCode);
    }

    public String printBriefInfo() {
        return new StringBuilder().append("resources: ")
                .append(this.resourceBean.stream().map(it -> it.getCode()).collect(Collectors.joining(",")))
                .append(", begin time: ").append(this.beginTime)
                .append(", metric size: ")
                .append(this.resourceBean.stream().mapToInt(it -> it.getMetrics().size()).sum()).toString();
    }
}
